﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Threading;

namespace Extented.UI.Core.Native
{
    public class Namescope : INamescope, IElementHost, IPropertyChangedListener
    {
        readonly Dictionary<string, FrameworkRenderElementContext> dictionary = new Dictionary<string, FrameworkRenderElementContext>();
        readonly List<RenderControlBaseContext> children = new List<RenderControlBaseContext>();
        readonly ValueChangedStorage valueChangedStorage = new ValueChangedStorage();
        readonly Queue<ChangeValueListenerTask> invalidateListeners = new Queue<ChangeValueListenerTask>();
        readonly IChrome chrome;
        Namescope Parent { get; set; }
        IChrome Chrome { get { return chrome ?? Parent.With(x => x.Chrome); } }
        public IEnumerable<RenderTriggerContextBase> Triggers { get; set; }
        public FrameworkRenderElementContext RootElement { get; set; }
        public ValueChangedStorage ValueChangedStorage { get { return valueChangedStorage; } }
        public FrameworkRenderElementContext GetElement(string name)
        {
            FrameworkRenderElementContext result;
            dictionary.TryGetValue(name, out result);
            return result;
        }
        public Namescope()
            : this(new Chrome())
        {
        }
        public Namescope(Namescope parent)
        {
            Parent = parent;
        }
        public Namescope(IChrome chrome)
        {
            this.chrome = chrome;
        }
        public void PropagateDeferredActions()
        {
            isUsed = false;
            if (invalidateListeners.Count == 0)
                return;
            do
            {
                ChangeValueListenerTask task = invalidateListeners.Dequeue();
                task.Action();
            }
            while (invalidateListeners.Count > 0);
        }
        bool isUsed = false;
        public void PropagateDeferredActionsAsync()
        {
            if (isUsed)
                return;
            isUsed = true;
            Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.DataBind, new Action(PropagateDeferredActions));
        }
        public void RegisterElement(FrameworkRenderElementContext context)
        {
            RegisterElementInternal(context);
            AddChild(context);
        }
        void RegisterElementInternal(FrameworkRenderElementContext context)
        {
            string name = context.Factory.Name;
            if (dictionary.ContainsKey(name))
                throw new ArgumentException(name);
            dictionary.Add(name, context);
        }
        public void ReleaseElement(FrameworkRenderElementContext element)
        {
            var context = ReleaseElementInternal(element.Name);
            RemoveChild(context);
        }
        FrameworkRenderElementContext ReleaseElementInternal(string name)
        {
            var context = dictionary[name];
            dictionary.Remove(name);
            return context;
        }
        public void AddChild(FrameworkRenderElementContext context)
        {
            if (!context.AttachToRoot || context.IsAttachedToRoot)
                return;
            var renderElement = context as RenderControlBaseContext;
            if (renderElement != null)
            {
                AddToTree(renderElement);
            }
            context.AttachToVisualTree((FrameworkElement)Chrome);
            context.IsAttachedToRoot = true;
        }
        void AddToTree(RenderControlBaseContext context)
        {
            if (Parent == null)
            {
                Chrome.AddChild(context.Control);
                children.Add(context);
                return;
            }
            Parent.AddToTree(context);
        }
        public void RemoveFromTree(RenderControlBaseContext context)
        {
            if (Parent == null)
            {
                Chrome.RemoveChild(context.Control);
                children.Remove(context);
                return;
            }
            Parent.RemoveFromTree(context);
        }
        public void RemoveChild(FrameworkRenderElementContext context)
        {
            if (!context.IsAttachedToRoot)
                return;
            var renderElement = context as RenderControlBaseContext;
            if (renderElement != null)
                RemoveFromTree(renderElement);
            context.DetachFromVisualTree((FrameworkElement)Chrome);
        }
        int IElementHost.ChildrenCount { get { return children.Count; } }
        FrameworkElement IElementHost.GetChild(int index) { return children[index].Control; }
        IEnumerator IElementHost.LogicalChildren { get { return children.Select(child => child.Control).GetEnumerator(); } }
        FrameworkElement IElementHost.TemplatedParent { get { return (FrameworkElement)((FrameworkElement)Chrome).TemplatedParent; } }
        FrameworkElement IElementHost.Parent { get { return (FrameworkElement)Chrome; } }
        void IElementHost.InvalidateMeasure()
        {
            Chrome.InvalidateMeasure();
            Chrome.InvalidateVisual();
        }
        void IElementHost.InvalidateArrange()
        {
            Chrome.InvalidateArrange();
        }
        void IElementHost.InvalidateVisual()
        {
            Chrome.InvalidateVisual();
        }
        public void SubscribeValueChangedAsync(RenderPropertyChangedListenerContext context)
        {
            invalidateListeners.Enqueue(new ChangeValueListenerTask() { Context = context, Action = context.SubscribeValue });
            PropagateDeferredActionsAsync();
        }
        public void UnsubscribeValueChanged(object target, RenderPropertyChangedListenerContext context)
        {
            valueChangedStorage.UnsubscribeValueChanged(target, context);
        }
        public void SubscribeValueChanged(object target, RenderPropertyChangedListenerContext context)
        {
            valueChangedStorage.SubscribeValueChanged(target, context);
        }
        public void Flush()
        {
            PropagateDeferredActions();
        }
        public void GoToState(string stateName)
        {
            Triggers.Do(coll => coll.OfType<RenderStateGroupContext>().ForEach(x => x.GoToState(stateName)));
        }
    }
}
